package ru.fadeev.crammer.ui.window;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import ru.fadeev.crammer.GFIndicesStorage;
import ru.fadeev.crammer.ProgressBar;
import ru.fadeev.crammer.TextFileFilter;
import ru.fadeev.crammer.Settings;
import ru.fadeev.crammer.data.PhrasesBatch;
import ru.fadeev.crammer.data.PhrasesCorpus;
import ru.fadeev.crammer.ui.ArrowOptionPane;
import ru.fadeev.crammer.ui.BasicCellRenderer;
import ru.fadeev.crammer.ui.ChooserTextFileFilter;
import ru.fadeev.crammer.ui.CrammerToolbarButton;
import ru.fadeev.crammer.ui.LimitedPlainDocument;


/**
 * SettingsDialog class provides UI and functionality for customizing
 * Crammer settings
 * @author Konstantin Fadeyev
 */
public class SettingsDialog extends JDialog 
		implements TreeSelectionListener, ProgressBar {
	// Constants
	public final static int BTN_CANCEL = 101;
	public final static int BTN_OK = 100;
	
	private final static String ERR_BANK_PORT = 
		"Port should be a positive integer";
	private final static String ERR_BASE = 
		"Base of time extension should be a float value between 0 and 10.0";
	private final static String ERR_CHECK_PERIOD = 
		"Test needs check period should be an integer >= 1";
	private final static String ERR_CUSTOM_DELIM = 
		"Custom delimiter should be valid regular expression";
	private final static String ERR_MIN_PHRASE_LEN = 
		"Minimum phrase length should be an integer between 3 and 15";
	private final static String ERR_NEEDED_TO_TEST_NUMBER = 
		"Number of untested words should be an integer >= 1";
	private final static String ERR_NO_DELIM = 
		"You should specify at least one delimiter";
	private final static String ERR_REPEAT_TIME = 
		"First repeat time should be an integer >= 0";		
	private final static String ERR_TEST_NUMBER = 
		"Number of lines in test dialog should be an " +
		"integer between 1 and 40";
	
	private final static String NAME_ADD_PHRASES = "Adding Phrases";
	private final static String NAME_CORPUS = "Phrases Corpus (local)";
	private final static String NAME_REVERSE = "Reverse Translation";
	private final static String NAME_TRANSLATION = "Translation";
	private final static String NAME_GAPS_FIL = "Gaps Filling";
	private final static String NAME_GENERAL = "General";
	private final static String NAME_PHRASES_BANK = "Phrases Bank (remote)";	
	private final static String NAME_TEST = "Test";	
	private final static String NAME_TRANS = "Transcription";
	
	private final static int PANEL_ADD_PHRASES = 6;
	private final static int PANEL_CORPUS = 5;
	private final static int PANEL_REVERSE = 3;
	private final static int PANEL_TRANSLATION = 2;
	private final static int PANEL_GAPS_FIL = 4;
	private final static int PANEL_GENERAL = 0;
	private final static int PANEL_NO = -1;
	private final static int PANEL_PHRASES_BANK = 8;
	private final static int PANEL_TEST = 1;			
	private final static int PANEL_TRANS = 7;
	
	private final static String REPEAT_ALG_DESCR = 
		"<html>Time, after word should be tested " + 
		"since last test, is defined by formula: " +
		"BasePeriod * (Factor^SuccessfulTestsNumber)." +
		"</html>";
	
	
	// UI Components
	private JLabel addPhraseCustomDelimiterLabel;
	private JTextField addPhraseCustomDelimiterTextField;
	private JPanel addPhraseDelimitersPanel;
	private JCheckBox addPhraseExclamationCheckBox;
	private JCheckBox addPhraseLFCheckBox;	
	private JLabel addPhraseMinLengthLabel;
	private JTextField addPhraseMinLengthTextField;
	private JCheckBox addPhrasePointCheckBox;
	private JCheckBox addPhrasePreprocessingCheckBox;	
	private JCheckBox addPhraseQuestionCheckBox;
	private JPanel bottomPanel;
	private JPanel bottomPanelTopBorder;
	private JPanel bottomPanelWithBorders;	
	private JButton cancelButton;
	private JPanel centerBottomPanel;
	private JPanel centerPanel;	
	private JPanel centerPanelLeftBorder;
	private JPanel centerPanelWithBorders;
	private JLabel checkPeriodExplanationLabel;
	private JLabel checkPeriodLabel;
	private JTextField checkPeriodTextField;	
	private JPanel checkTestPanel;
	private JButton corpusAddDirectoryButton;
	private JButton corpusAddFileButton;
	private JButton corpusAddTextButton;
	private JPanel corpusBottomPanel;		
	private JButton corpusDeleteButton;
	private JButton corpusEditButton;
	private JButton corpusExportButton;
	private JLabel corpusPhrasesCountLabel;
	private JButton corpusRefreshButton;
	private JScrollPane corpusScrollPane;
	private JTable corpusTable;
	private PhrasesCorpusTableModel corpusTableModel;
	private JToolBar corpusToolBar;	
	private JLabel reverseAlgorithmLabel;
	private JLabel reverseFactorLabel;
	private JTextField reverseFactorTextField;
	private JLabel reverseBasePeriodLabel;
	private JTextField reverseBasePeriodTextField;
	private JLabel reverseLinesNumberLabel;
	private JTextField reverseLinesNumberTextField;
	private JPanel reverseParametersPanel;
	private JLabel translationAlgorithmLabel;	
	private JLabel translationFactorLabel;
	private JTextField translationFactorTextField;
	private JLabel translationBasePeriodLabel;
	private JTextField translationBasePeriodTextField;
	private JLabel translationLinesNumberLabel;
	private JTextField translationLinesNumberTextField;
	private JPanel translationParametersPanel;
	private JButton defaultButton;
	private JPanel defaultButtonPanel;	
	private DocumentListener documentListener;
	private JLabel errorLabel;
	private JLabel gapsFillingAlgorithmLabel;
	private JLabel gapsFillingFactorLabel;
	private JTextField gapsFillingFactorTextField;
	private JLabel gapsFillingBasePeriodLabel;
	private JTextField gapsFillingBasePeriodTextField;
	private JLabel gapsFillingLinesNumberLabel;
	private JTextField gapsFillingLinesNumberTextField;
	private JPanel gapsFillingParametersPanel;
	private JCheckBox loadPhrasesFromBankCheckBox;
	private JPanel mainPanel;	
    private JCheckBox minimizeOnCloseCheckBox;
    private JCheckBox minimizeToTrayCheckBox;
    private JLabel neededToTestWindowExplanationLabel;
    private JLabel neededToTestWindowNumberLabel;
    private JTextField neededToTestWindowNumberTextField;
    private JPanel notifyTestPanel;
    private JButton okButton;
	private JPanel okCancelButtonsPanel;
    private JLabel phrasesBankPortLabel;
	private JTextField phrasesBankPortTextField;
	private JPanel phrasesBankProxyPanel;
	private JLabel phrasesBankProxyPortLabel;
	private JTextField phrasesBankProxyPortTextField;	
	private JLabel phrasesBankProxyServerLabel;
	private JTextField phrasesBankProxyServerTextField;
	private JLabel phrasesBankServerLabel;
	private JPanel phrasesBankServerPanel;
	private JTextField phrasesBankServerTextField;
	private JCheckBox phrasesBankUseProxyCheckBox;
	private JProgressBar progressBar;
	private JLabel progressBarLabel;
	private JPanel progressBarPanel;
	private JPanel settingsAddPhrasePanel;
	private JPanel settingsCorpusPanel;	
	private JPanel settingsReversePanel;
	private JPanel settingsTranslationPanel;
	private JPanel settingsGapsFillingPanel;
	private JPanel settingsGeneralPanel;
	private JLabel settingsNameLabel;
	private JPanel settingsNamePanel;
	private JPanel settingsNamePanelBottomBorder;
	private JPanel settingsNamePanelWithBorders;
	private JPanel settingsPanel;
	private JPanel settingsPhrasesBankPanel;
	private JPanel settingsTestPanel;
	private JPanel settingsTranscriptionPanel;
	private JTree settingsTree;
	private JCheckBox showNeededToTestWindowCheckBox;
	private JButton testPhrasesBankButton;
	private JLabel testPhrasesBankLabel;
	private JLabel transcriptionAlgorithmLabel;
	private JLabel transcriptionFactorLabel;
	private JTextField transcriptionFactorTextField;	
	private JLabel transcriptionBasePeriodLabel;
	private JTextField transcriptionBasePeriodTextField;
	private JLabel transcriptionLinesNumberLabel;
	private JTextField transcriptionLinesNumberTextField;
    private JPanel transcriptionParametersPanel;
    private JScrollPane treeScrollPane;
	
    
	// User fields
    private PhrasesCorpus corpus;
	private int currentPanel;
	private int pressedButton;
	private boolean processing;
	private int progressBarValue;
    private GFIndicesStorage storage;	
    
    
    /**
     * Constructs SettingsDialog
     * @param owner parent window
     * @param storage object of GFIndicesStorage
     */
    public SettingsDialog(JFrame owner, GFIndicesStorage storage) {
        super(owner, true);

        this.storage = storage;
        corpus = new PhrasesCorpus();
        processing = false;
        pressedButton = BTN_CANCEL;
        currentPanel = PANEL_NO;
        
    	int x = Settings.getInt("settingsDialogX");
    	int y = Settings.getInt("settingsDialogY");
    	int w = Settings.getInt("settingsDialogW");
    	int h = Settings.getInt("settingsDialogH");	
        if(x<0 || y<0 || w<0 || h<0) {
        	Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        	setSize(590, 400);
            setLocation((screenSize.width-580)/2, (screenSize.height-340)/3);
        } else {
        	setSize(w, h);
        	setLocation(x, y);        	
        }
        
        setTitle("Settings");
        initComponents();
        placeComponents();
        settingsTree.setSelectionRow(0);
    }
    

    /**
     * On Phrases Corpus Panel when user's adding phrases by one of three
     * ways - from file, from directory or by hand, found valid phrases are
     * collected to batch. This function checks if batch's not empty and add
     * batch to corpus, then shows message about how many phrases was added 
     * and refreshes phrasesBatch table.
     * @param phrasesCount number of phrases to add
     * @param newBatch batch, where found phrases is stored
     */
    private void afterAddingPhraseAction(int phrasesCount, 
    		PhrasesBatch newBatch) {
   		if (phrasesCount == 0) {
   			JOptionPane.showMessageDialog(this,
   					"There is no suitable phrases in the specified text.\n" +
   	                "No batch will be added.","Error", JOptionPane.ERROR_MESSAGE);
   			newBatch.deleteFromDB(storage);
   		} else {
   			JOptionPane.showMessageDialog(this,
   					phrasesCount + " phrases added to the corpus successfully.",
   	                "Message", JOptionPane.INFORMATION_MESSAGE);    
   			newBatch.setPhrasesCount(phrasesCount);
   			corpus.addBatch(newBatch);
   			fireCorpusTableDataChanged();
   			corpusTable.setRowSelectionInterval(corpusTable.getRowCount() - 1, 
   					corpusTable.getRowCount() - 1);
   		}   	
    }
   
    
    /**
     * Cancel button action handler. Closes dialog
     */
    private void cancelButtonAction() {
        pressedButton = BTN_CANCEL;
        setVisible(false);	
    }
       
    
    /**
     * Launches open directory dialog and, if dialog returns directory, adds
     * phrases from all text file (.txt) from this directory and its 
     * subdirectories to phrases corpus.
     */
    private void corpusAddDirectory() {
        if(processing) {
    		return;
    	}
    	JFileChooser chooser = new JFileChooser();
    	File startDirectory = new File(Settings.getString("lastAddedPhrasesDirectory"));
    	if(startDirectory.exists()) {
    		chooser.setCurrentDirectory(startDirectory);
    	} else {
    		chooser.setCurrentDirectory(new File("."));
    	}
    	chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

    	int returnVal = chooser.showOpenDialog(this);
    	if (returnVal == JFileChooser.APPROVE_OPTION) {
    		final File directory = chooser.getSelectedFile();
    		Settings.setString("lastAddedPhrasesDirectory", 
    				directory.getParent());
    		final PhrasesBatch newBatch = specifyBatchName(directory.getName());
    		final SettingsDialog thisObject = this;
    		if(newBatch != null) {
    			Thread thread = new Thread() {
    				public void run() {
		    			newBatch.insertToDB();
		    			   	   	
		    	   		showPBDeterminate(getDirectoryLength(directory), 
		    	   				"processing...");	    	   			
		    			int phrasesCount = newBatch.insertDirectoryToDB(
		    					directory, thisObject, storage);
		    			hidePB();
		    	   		afterAddingPhraseAction(phrasesCount, newBatch);
		    	   		processing = false;
		    	   	}
		    	};
		    	processing = true;
		    	thread.start();				    	   		
    	   	}
    	}
    }
    
    
    /**
     * Launches open text file (.txt) dialog and, if dialog returns file, adds
     * phrases from this file to phrases corpus.
     */
    private void corpusAddFile() {
        if(processing) {
    		return;
    	}
    	
    	JFileChooser chooser = new JFileChooser();
    	File startDirectory = 
    			new File(Settings.getString("lastAddedPhrasesDirectory"));
    	if(startDirectory.exists()) {
    		chooser.setCurrentDirectory(startDirectory);
    	} else {
    		chooser.setCurrentDirectory(new File("."));
    	}
    	chooser.setFileFilter(new ChooserTextFileFilter());
    	
    	int returnVal = chooser.showOpenDialog(this);
    	if (returnVal == JFileChooser.APPROVE_OPTION) {
    		final File file = chooser.getSelectedFile();
    		Settings.setString("lastAddedPhrasesDirectory", file.getParent());
    		final PhrasesBatch newBatch = specifyBatchName(file.getName());
    		final SettingsDialog thisObject = this;
    		if(newBatch != null) {
    			Thread thread = new Thread() {
    				public void run() {
    					newBatch.insertToDB();
    					
    					showPBDeterminate((int)file.length(), "processing..."); 	
		    	    	int phrasesCount = newBatch.insertFileToDB(file, 
		    	    			thisObject, storage);
		    	    	hidePB();
		    	    	afterAddingPhraseAction(phrasesCount, newBatch);
		    	   		processing = false;	    	   		
		    	   	}
		    	};
		    	processing = true;
		    	thread.start();		    	    	
    	   	}
    		
    	}
    }
    
    
    /**
     * Launches add text dialog and, if dialog returns text, adds
     * phrases compiled from this text to phrases corpus.
     */
    private void corpusAddText() {
    	if(processing) {
    		return;
    	}
    
		final AddCorpusTextDialog addTextDialog = new AddCorpusTextDialog(this);
    	addTextDialog.setVisible(true);
    	
    	if(addTextDialog.getPressedButton() == AddCorpusTextDialog.BTN_OK) {
    		final PhrasesBatch newBatch = specifyBatchName("");
    		final SettingsDialog thisObject = this;
    		if(newBatch != null) {
    			Thread thread = new Thread() {
    				public void run() {
    					newBatch.insertToDB();
		    			String text = addTextDialog.getText();   			
		    			showPBDeterminate(text.length(), "processing...");
		 	   			int phrasesCount = newBatch.insertTextToDB(text, 
		 	   				thisObject, storage);
		    	   		hidePB();
		    	   		afterAddingPhraseAction(phrasesCount, newBatch);
		    	   		processing = false;
		    	   	}
		    	};
		    	processing = true;
		    	thread.start();
    	   	}
    	}
    }
    
    
    /**
     * Deletes selected phrases batches from phrases corpus
     */
    private void corpusDeleteBatches() {
		int n = ArrowOptionPane.showConfirmDialog(this,
				"Selected batches of phrases will be deleted.\n" +
                "Are you sure?","Are you sure?", JOptionPane.YES_NO_OPTION);
	    switch(n)
	    {
	        case JOptionPane.OK_OPTION:        
	        	int[] indices = corpusTable.getSelectedRows();
	        	int columnIndex = corpusTable.getSelectedColumn();
	        	
	        	int indexToSelect = indices[indices.length-1] 
	        	                            - indices.length + 1;
	        	
	        	corpus.removeBatches(indices, storage);
	        	fireCorpusTableDataChanged();
				if( (indexToSelect == corpus.size()))
				{
					indexToSelect = indexToSelect - 1;
				}
				if(indexToSelect >= 0)
				{
					corpusTable.setRowSelectionInterval(indexToSelect, 
							indexToSelect);
				}
				if(columnIndex >= 0)
				{
					corpusTable.setColumnSelectionInterval(columnIndex, 
							columnIndex);
				}
				corpusTable.requestFocus();
				
	            break;
	        default:       // nothing to do
	            return;
	    }    	
    }
    
    
    /**
     * Default button action handler. Sets fields on current panel
     * to default values.
     */
    private void defaultButtonAction() {
		switch(currentPanel) {
		case PANEL_GENERAL: 
			minimizeToTrayCheckBox.setSelected(
					Settings.getDefaultBoolean("minimizeToTray"));
			minimizeOnCloseCheckBox.setSelected(
					Settings.getDefaultBoolean("minimizeOnClose"));
			break;
		case PANEL_TEST: 
			checkPeriodTextField.setText(Long.toString((
					Settings.getDefaultLong("testNeedsCheckPeriod")/60000)));
			neededToTestWindowNumberTextField.setText(
						Settings.getDefaultString("neededToTestWindowNumber"));
			showNeededToTestWindowCheckBox.setSelected(
					Settings.getDefaultBoolean("showNeededToTestWindow"));
			break;
		case PANEL_TRANSLATION:
			translationLinesNumberTextField.setText(
						Settings.getDefaultString("translationNumberOfLines"));
			translationBasePeriodTextField.setText(
	    			Long.toString((
	    					Settings.getDefaultLong("translationBasePeriod")/60000)));    	
			translationFactorTextField.setText(
					Settings.getDefaultString("translationFactor"));
			break;
		case PANEL_REVERSE:
			reverseLinesNumberTextField.setText(
					Settings.getDefaultString("reverseNumberOfLines"));
			reverseBasePeriodTextField.setText(
	    			Long.toString((
	    					Settings.getDefaultLong("reverseBasePeriod")/60000)));    	
			reverseFactorTextField.setText(
					Settings.getDefaultString("reverseFactor"));
			break;
		case PANEL_TRANS:
			transcriptionLinesNumberTextField.setText(
					Settings.getDefaultString("transcriptionNumberOfLines"));
			transcriptionBasePeriodTextField.setText(
	    			Long.toString((
	    					Settings.getDefaultLong("transcriptionBasePeriod")/60000)));    	
			transcriptionFactorTextField.setText(
					Settings.getDefaultString("transcriptionFactor"));
			break;
		case PANEL_GAPS_FIL:
			gapsFillingLinesNumberTextField.setText(
					Settings.getDefaultString("gapsFillingNumberOfLines"));
			gapsFillingBasePeriodTextField.setText(
	    			Long.toString((
	    					Settings.getDefaultLong("gapsFillingBasePeriod")/60000)));    	
			gapsFillingFactorTextField.setText(
					Settings.getDefaultString("gapsFillingFactor"));
			break;
		case PANEL_ADD_PHRASES:
			addPhrasePointCheckBox.setSelected(
					Settings.getDefaultBoolean("addPhrasePoint"));
    		addPhraseExclamationCheckBox.setSelected(
    				Settings.getDefaultBoolean("addPhraseExclamation"));
    		addPhraseQuestionCheckBox.setSelected(
    				Settings.getDefaultBoolean("addPhraseQuestion"));
    		addPhraseLFCheckBox.setSelected(
    				Settings.getDefaultBoolean("addPhraseLF"));
    		addPhraseCustomDelimiterTextField.setText(
    				Settings.getDefaultString("addPhraseCustomDelimiter"));
    		addPhraseMinLengthTextField.setText(
    				Settings.getDefaultString("addPhraseMinLength"));
    		addPhrasePreprocessingCheckBox.setSelected(
    				Settings.getDefaultBoolean("addPhrasePreprocessing"));
    		break;
		case PANEL_PHRASES_BANK:
			loadPhrasesFromBankCheckBox.setSelected(
    				Settings.getDefaultBoolean("loadPhrasesFromBankForNewWords"));
			phrasesBankServerTextField.setText(
    				Settings.getDefaultString("phrasesBankServer"));
			phrasesBankPortTextField.setText(
    				Settings.getDefaultString("phrasesBankPort"));
			phrasesBankUseProxyCheckBox.setSelected(
    				Settings.getDefaultBoolean("phrasesBankUseProxy"));
			phrasesBankProxyServerTextField.setText(
    				Settings.getDefaultString("phrasesBankProxyServer"));
			phrasesBankProxyPortTextField.setText(
    				Settings.getDefaultString("phrasesBankProxyPort"));
			break;
		default:
		}    	
    }
       
    
    /**
     * Launches EditBatchDialog for currently selected phrases batch
     */
    private void editBatch() {
    	int index = corpusTable.getSelectedRow();
    	PhrasesBatch batch = corpus.getBatch(index);
		EditBatchDialog dialog = new EditBatchDialog(this, batch);
		dialog.setVisible(true);    	
		
    	if(dialog.getPressedButton() == EditBatchDialog.BTN_OK) {
    		batch.removePhrasesFromDB(dialog.getDeletedPhrases(), storage);
    		batch.savePhrases(storage);
    		batch.unloadPhrases();
        	batch.update();
    		fireCorpusTableDataChanged();    		
			corpusTable.setRowSelectionInterval(index, index);
    	}
    }
       
    
    /**
     * Exports phrases to text file. File will consist one phrase per line.
     */
    private void exportPhrases() {
        if(processing) {
    		return;
    	}
        
    	JFileChooser chooser = new JFileChooser();
    	File startDirectory = 
    			new File(Settings.getString("lastAddedPhrasesDirectory"));
    	if(startDirectory.exists()) {
    		chooser.setCurrentDirectory(startDirectory);
    	} else {
    		chooser.setCurrentDirectory(new File("."));
    	}
    	chooser.setFileFilter(new ChooserTextFileFilter());
    	
    	int returnVal = chooser.showSaveDialog(this);
    	if (returnVal == JFileChooser.APPROVE_OPTION) {
    		File tempFile = chooser.getSelectedFile();
    		
        	ChooserTextFileFilter textFilter = new ChooserTextFileFilter();
        	if(chooser.getFileFilter().getDescription().equals(
        			textFilter.getDescription()) ) {
        		if(! tempFile.getName().endsWith(".txt")) {
        			String fileName =  tempFile.getName() + ".txt";
        			tempFile = new File(tempFile.getParent(), fileName);
        		}
        	}
        	final File file = tempFile;

    		Settings.setString("lastAddedPhrasesDirectory", file.getParent());
    		final SettingsDialog thisObject = this;
			Thread thread = new Thread() {
				public void run() {
					showPBDeterminate(corpus.getPhrasesCount(), "exporting..."); 	
	    	   		corpus.exportBatchesToFile(file, 
	    	   				corpusTable.getSelectedRows(), thisObject);
	    	    	hidePB();
	    	   		processing = false;	    	   		
	    	   	}
	    	};
	    	processing = true;
	    	thread.start();
    	}
    }
       
    
    /**
     * Refreshes corpus table data and phrases count label
     */
    private void fireCorpusTableDataChanged() {
    	corpusTableModel.fireTableDataChanged();
    	corpusPhrasesCountLabel.setText("Total phrases count: " 
    			+ corpus.getPhrasesCount() + "  ");
    }

    
    /**
     * Returns total size of all text files in directory and all its 
     * subdirectories. Function is used to estimate duration of adding 
     * directory to phrases corpus
     * @param directory directory for adding to phrases corpus
     * @return total size of all text files
     */
    private int getDirectoryLength(File directory) {
      	TextFileFilter fileFilter = new TextFileFilter();
   	    
   		File[] files = directory.listFiles(fileFilter);
   		int count = 0;
   		for (int i = 0; i < files.length; i++) {
   			if(files[i].isDirectory()) {
   				count += getDirectoryLength(files[i]);
   			} else {
   				count += files[i].length();
   			}
   		}    	
   		return count;
    }   
    
    
    /**
	 * Implements ProgressBar interface getPBValue function
	 */
    public int getPBValue() {
    	return progressBar.getValue();
    }
    
    
    /**
     * After dialog is closed, call this function to find out which option
     * user picked (OK or Cancel)
     * @return pressed button (one of BTN_OK, BTN_CANCEL)
     */
    public int getPressedButton() {
    	return pressedButton;
    }
      
    
	/**
	 * Implements ProgressBar Interface hidePB function
	 */
	public void hidePB() {
		progressBar.setVisible(false);
		progressBarLabel.setVisible(false);	
	}
    
    /**
     * Initializes AddPhrase panel
     */
    private void initAddPhrasePanel() {
    	settingsAddPhrasePanel = new JPanel();
    	addPhraseDelimitersPanel = new JPanel();
    	addPhraseDelimitersPanel.setBorder(BorderFactory.
				createTitledBorder(BorderFactory.
				createEtchedBorder(), "Delimiters"));
    	addPhrasePointCheckBox = new JCheckBox("point",
    			Settings.getBoolean("addPhrasePoint"));
    	addPhraseExclamationCheckBox = new JCheckBox("exclamation mark",
    			Settings.getBoolean("addPhraseExclamation"));
    	addPhraseQuestionCheckBox = new JCheckBox("question mark",
    			Settings.getBoolean("addPhraseQuestion"));
    	addPhraseLFCheckBox = new JCheckBox("line feed");
   			
    	addPhraseCustomDelimiterLabel = new JLabel("Custom delimiter:");
    	addPhraseCustomDelimiterTextField = new JTextField();
    	LimitedPlainDocument addPhraseCustomDelimiterDocument = 
				new LimitedPlainDocument(10);
    	addPhraseCustomDelimiterDocument.addDocumentListener(documentListener);
    	addPhraseCustomDelimiterTextField.setDocument(
    			addPhraseCustomDelimiterDocument);
    	addPhraseCustomDelimiterTextField.setText(
    			Settings.getString("addPhraseCustomDelimiter"));
    	addPhraseMinLengthLabel = 
    			new JLabel("Minimal phrase length (in words):");
    	addPhraseMinLengthTextField = new JTextField();
    	LimitedPlainDocument addPhraseMinLengthDocument = 
				new LimitedPlainDocument(2);
    	addPhraseMinLengthDocument.addDocumentListener(documentListener);
    	addPhraseMinLengthTextField.setDocument(addPhraseMinLengthDocument);
    	addPhraseMinLengthTextField.setText(
    			Settings.getString("addPhraseMinLength"));			
    	addPhrasePreprocessingCheckBox = new JCheckBox(
    			"enable file preprocessing (removing some junk)",
    			Settings.getBoolean("addPhrasePreprocessing"));    			
    			
		ItemListener checkBoxSelectionListener = 
			new ItemListener(){
				public void itemStateChanged(ItemEvent e) {
					validateCurrentPanel();
				}
			}; 			
    	addPhrasePointCheckBox.addItemListener(checkBoxSelectionListener);
    	addPhraseExclamationCheckBox.addItemListener(checkBoxSelectionListener);
    	addPhraseQuestionCheckBox.addItemListener(checkBoxSelectionListener);
    	addPhraseLFCheckBox.addItemListener(checkBoxSelectionListener);
    	addPhraseLFCheckBox.setSelected(Settings.getBoolean("addPhraseLF"));
    }
       
    /**
     * Initializes UI Components of Settings dialog
     */
    private void initComponents() {   
    	// We initialize documentListener to add this listener to all 
    	// documents that could be validated so dialog validates it when its
    	// value changes
    	documentListener = new DocumentListener() {		
    		public void changedUpdate(DocumentEvent arg0) {}

    		public void insertUpdate(DocumentEvent event) {
    			validateCurrentPanel();
    		}

    		public void removeUpdate(DocumentEvent event) {
    			validateCurrentPanel();
    		}
    	};
    	
    	// Some panels and labels
        mainPanel = new JPanel();
        
        centerPanel = new JPanel();
    	centerPanelWithBorders = new JPanel();
    	centerPanelLeftBorder = new JPanel();
    	centerPanelLeftBorder.setBorder(BorderFactory.createEtchedBorder());
    	
    	settingsNamePanel = new JPanel();
    	settingsNamePanelWithBorders = new JPanel();
    	settingsNamePanelBottomBorder = new JPanel();
    	settingsNamePanelBottomBorder.setBorder(BorderFactory.createEtchedBorder());

    	settingsPanel = new JPanel();  	
    
    	settingsReversePanel = new JPanel();
    	
    	settingsNameLabel = new JLabel();
    	Font labelFont = settingsNameLabel.getFont();
    	settingsNameLabel.setFont(labelFont.deriveFont(Font.BOLD,14.0f));
    	
    	ImageIcon errorIcon = new ImageIcon("icons/16/error.png");
    	errorLabel = new JLabel(errorIcon);
    	errorLabel.setHorizontalAlignment(JLabel.LEFT);
    	errorLabel.setVisible(false);
    	
    	centerBottomPanel = new JPanel();
    	defaultButtonPanel = new JPanel();
    	defaultButton = new JButton("Restore Defaults");
    	defaultButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	defaultButtonAction();
			        }
			    });
    	
    	// Custom Settings Panels
    	initGeneralPanel();
    	initTestPanel();
    	initTranslationPanel();
    	initReversePanel();
    	initTranscriptionPanel();
    	initGapsFillingPanel();
    	initCorpusPanel();
    	initAddPhrasePanel();
    	initPhrasesBankPanel();
    	
        // Tree
    	settingsTreeInit();
                
        // Ok, Cancel buttons
        bottomPanel = new JPanel();
        bottomPanelWithBorders = new JPanel();
        bottomPanelTopBorder = new JPanel();
        bottomPanelTopBorder.setBorder(BorderFactory.createEtchedBorder());
        okCancelButtonsPanel = new JPanel(); 
        okButton = new JButton("OK");
        getRootPane().setDefaultButton(okButton);
        
		cancelButton = new JButton("Cancel");
        
        okButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	okButtonAction();
			        }
			    });
        
        cancelButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			           	cancelButtonAction();
			        }
		        });
        setContentPane(mainPanel);
    }
       
    
    /**
     * Initializes Phrases Corpus panel
     */
    private void initCorpusPanel() {
    	settingsCorpusPanel = new JPanel();
    	corpusBottomPanel = new JPanel();
    	corpusPhrasesCountLabel = new JLabel();
    	progressBarPanel = new JPanel();
    	progressBarLabel = new JLabel();
		progressBar = new JProgressBar();
		hidePB();
    	
        // Buttons for operations with batches of phrases
		ImageIcon addFileIcon = new ImageIcon("icons/16/addFile.png");
		corpusAddFileButton = new CrammerToolbarButton(addFileIcon, 
				"Add phrases from file");
		corpusAddFileButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	corpusAddFile();
			        }
			    });

		ImageIcon addDirectoryIcon = new ImageIcon("icons/16/addDirectory.png");
		corpusAddDirectoryButton = new CrammerToolbarButton(addDirectoryIcon, 
				"Add phrases from all text files in directory");
		corpusAddDirectoryButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	corpusAddDirectory();
			        }
			    });
		
		ImageIcon addTextIcon = new ImageIcon("icons/16/addText.png");
		corpusAddTextButton = new CrammerToolbarButton(addTextIcon, 
				"Add phrases by hand");
		corpusAddTextButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	corpusAddText();
			        }
			    });
		
		ImageIcon deleteIcon = new ImageIcon("icons/16/delete.png");
        corpusDeleteButton = new CrammerToolbarButton(deleteIcon, 
        		"Delete selected batches of phrases");
        corpusDeleteButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	corpusDeleteBatches();
			        }
			    });

		ImageIcon editIcon = new ImageIcon("icons/16/renameBatch.png");
		corpusEditButton = new CrammerToolbarButton(editIcon, "Edit batch");
		corpusEditButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	editBatch();
			        }
			    });
		
		ImageIcon exportIcon = new ImageIcon("icons/16/exportPhrases.png");
		corpusExportButton = new CrammerToolbarButton(exportIcon, 
				"Export selected batches to text file");
		corpusExportButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	exportPhrases();
			        }
			    });
		
		ImageIcon refreshIcon = new ImageIcon("icons/16/refresh.png");
		corpusRefreshButton = new CrammerToolbarButton(refreshIcon, 
				"Refresh batches statistics");
		corpusRefreshButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	refreshStatistics();
			        }
			    });
		
        // ToolBar
        corpusToolBar = new JToolBar(JToolBar.HORIZONTAL);
        corpusToolBar.setFloatable(false);
        
        corpusToolBar.add(corpusAddFileButton);
		corpusToolBar.add(corpusAddDirectoryButton);
        corpusToolBar.add(corpusAddTextButton);
		corpusToolBar.add(corpusEditButton);
		corpusToolBar.add(corpusDeleteButton);
		corpusToolBar.addSeparator();
		corpusToolBar.add(corpusExportButton);
		corpusToolBar.addSeparator();
		corpusToolBar.add(corpusRefreshButton);
		
        // Batches table	
        corpusTableModel = new PhrasesCorpusTableModel(corpus);       
        corpusTable = new JTable(corpusTableModel);

        PhrasesCorpusSelectionListener batchesTableListener 
				= new PhrasesCorpusSelectionListener(this);
        corpusTable.getSelectionModel().addListSelectionListener(batchesTableListener);
        corpusTable.getColumnModel().getSelectionModel()
				.addListSelectionListener(batchesTableListener);

        corpusTable.getColumnModel().getColumn(0).setPreferredWidth(250);
        corpusTable.getColumnModel().getColumn(1).setPreferredWidth(140);
        corpusTable.getColumnModel().getColumn(2).setPreferredWidth(100);
        
        BasicCellRenderer cellRenderer = new BasicCellRenderer();
        corpusTable.setDefaultRenderer(String.class, cellRenderer);
        corpusTable.setDefaultRenderer(Integer.class, cellRenderer);
    	
        corpusTable.getTableHeader().setReorderingAllowed(false);   	
        
        corpusTable.addFocusListener(
        new FocusAdapter() {
        	public void focusGained(FocusEvent fe) {
        		if( (corpusTable.getSelectedRow() < 0 ) &&
        			(corpusTable.getSelectedColumn() < 0) && 
        			corpusTable.getRowCount() > 0) {
        			corpusTable.setRowSelectionInterval(0, 0);	
        			corpusTable.setColumnSelectionInterval(0, 0);	
        		}
        	}
        });
        
        corpusScrollPane = new JScrollPane(corpusTable);    	
    }
    
    
    /**
     * Initializes Reverse panel
     */
    private void initReversePanel() {
    	settingsReversePanel = new JPanel();
    	
    	reverseLinesNumberLabel = 
    			new JLabel("Number of lines shown in test dialog:");
    	reverseLinesNumberTextField = new JTextField();    	
    	LimitedPlainDocument reverseLinesNumberDocument = 
    			new LimitedPlainDocument(2);
    	reverseLinesNumberDocument.addDocumentListener(documentListener);
    	reverseLinesNumberTextField.setDocument(
    			reverseLinesNumberDocument);
    	reverseLinesNumberTextField.setText(
    			Settings.getString("reverseNumberOfLines"));
    	
    	reverseParametersPanel = new JPanel();
    	reverseParametersPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), 
    								"Repeat time parameters"));
    	reverseAlgorithmLabel = new JLabel(REPEAT_ALG_DESCR);
    	reverseBasePeriodLabel = 
    			new JLabel("BasePeriod (in minutes):");
    	reverseBasePeriodTextField = new JTextField();
    	LimitedPlainDocument reverseBasePeriodDocument = 
				new LimitedPlainDocument(10);
    	reverseBasePeriodDocument.addDocumentListener(
    			documentListener);
    	reverseBasePeriodTextField.setDocument(
    			reverseBasePeriodDocument);
    	reverseBasePeriodTextField.setText(
    			Long.toString((
    					Settings.getLong("reverseBasePeriod")/60000)));    	
    	
    	reverseFactorLabel = new JLabel("Factor:");
    	reverseFactorTextField = new JTextField();
    	LimitedPlainDocument reverseFactorDocument = 
				new LimitedPlainDocument(10);
    	reverseFactorDocument.addDocumentListener(
    			documentListener);
    	reverseFactorTextField.setDocument(
    			reverseFactorDocument);
    	reverseFactorTextField.setText(
    			Settings.getString("reverseFactor"));
    }
    
    
    /**
     * Initializes Translation panel
     */
    private void initTranslationPanel() {
    	settingsTranslationPanel = new JPanel();
    	
    	translationLinesNumberLabel = 
    			new JLabel("Number of lines shown in test dialog:");
    	translationLinesNumberTextField = new JTextField();    	
    	LimitedPlainDocument translationLinesNumberDocument = 
    			new LimitedPlainDocument(2);
    	translationLinesNumberDocument.addDocumentListener(documentListener);
    	translationLinesNumberTextField.setDocument(
    			translationLinesNumberDocument);
    	translationLinesNumberTextField.setText(
    			Settings.getString("translationNumberOfLines"));
    	
    	translationParametersPanel = new JPanel();
    	translationParametersPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), 
    								"Repeat time parameters"));
    	translationAlgorithmLabel = new JLabel(REPEAT_ALG_DESCR);
    	translationBasePeriodLabel = 
    			new JLabel("BasePeriod (in minutes):");
    	translationBasePeriodTextField = new JTextField();
    	LimitedPlainDocument translationBasePeriodDocument = 
    			new LimitedPlainDocument(10);
    	translationBasePeriodDocument.addDocumentListener(
    			documentListener);
    	translationBasePeriodTextField.setDocument(
    			translationBasePeriodDocument);
    	translationBasePeriodTextField.setText(
    			Long.toString((
    					Settings.getLong("translationBasePeriod")/60000)));    	
    	
    	translationFactorLabel = new JLabel("Factor:");
    	translationFactorTextField = new JTextField();
    	LimitedPlainDocument translationFactorDocument = 
				new LimitedPlainDocument(10);
    	translationFactorDocument.addDocumentListener(
    			documentListener);
    	translationFactorTextField.setDocument(
    			translationFactorDocument);
    	translationFactorTextField.setText(
    			Settings.getString("translationFactor"));
    }

    
    /**
     * Initializes GapsFilling panel
     */
    private void initGapsFillingPanel() {
    	settingsGapsFillingPanel = new JPanel();
    	
    	gapsFillingLinesNumberLabel = new JLabel("Number of lines shown in test dialog:");
    	gapsFillingLinesNumberTextField = new JTextField();    	
    	LimitedPlainDocument gapsFillingLinesNumberDocument = new LimitedPlainDocument(2);
    	gapsFillingLinesNumberDocument.addDocumentListener(documentListener);
    	gapsFillingLinesNumberTextField.setDocument(gapsFillingLinesNumberDocument);
    	gapsFillingLinesNumberTextField.setText(
    			Settings.getString("gapsFillingNumberOfLines"));
    	
    	gapsFillingParametersPanel = new JPanel();
    	gapsFillingParametersPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), "Repeat time parameters"));
    	gapsFillingAlgorithmLabel = new JLabel(REPEAT_ALG_DESCR);
    	gapsFillingBasePeriodLabel = new JLabel("BasePeriod (in minutes):");
    	gapsFillingBasePeriodTextField = new JTextField();
    	LimitedPlainDocument gapsFillingBasePeriodDocument = 
				new LimitedPlainDocument(10);
    	gapsFillingBasePeriodDocument.addDocumentListener(documentListener);
    	gapsFillingBasePeriodTextField.setDocument(gapsFillingBasePeriodDocument);
    	gapsFillingBasePeriodTextField.setText(
    			Long.toString((
    					Settings.getLong("gapsFillingBasePeriod")/60000)));    	
	
    	gapsFillingFactorLabel = new JLabel("Factor:");
    	gapsFillingFactorTextField = new JTextField();
    	LimitedPlainDocument gapsFillingFactorDocument = 
				new LimitedPlainDocument(10);
    	gapsFillingFactorDocument.addDocumentListener(documentListener);
    	gapsFillingFactorTextField.setDocument(gapsFillingFactorDocument);
    	gapsFillingFactorTextField.setText(
    			Settings.getString("gapsFillingFactor"));
    }

    
    /**
     * Initializes General Settings panel
     */
    private void initGeneralPanel() {
    	settingsGeneralPanel = new JPanel();
    	minimizeToTrayCheckBox = 
			new JCheckBox("minimize window to system tray", 
	    			Settings.getBoolean("minimizeToTray"));
    	minimizeOnCloseCheckBox = 
			new JCheckBox("minimize window on close", 
	    			Settings.getBoolean("minimizeOnClose"));
    }

    /**
     * Initializes Phrases Bank panel
     */
    private void initPhrasesBankPanel() {
    	loadPhrasesFromBankCheckBox = new JCheckBox(
    			"automatically load phrases from bank for new words", 
    			Settings.getBoolean("loadPhrasesFromBankForNewWords"));    	
    	
    	settingsPhrasesBankPanel = new JPanel();
    	phrasesBankServerPanel = new JPanel();
    	phrasesBankServerPanel.setBorder(BorderFactory.
				createTitledBorder(BorderFactory.
				createEtchedBorder(), "Phrases Bank Server"));	
  			
    	phrasesBankServerLabel = new JLabel("Server:");
    	phrasesBankServerTextField = new JTextField();
    	LimitedPlainDocument phrasesBankServerDocument = 
			new LimitedPlainDocument(200);
    	phrasesBankServerDocument.addDocumentListener(documentListener);
    	phrasesBankServerTextField.setDocument(phrasesBankServerDocument);
    	phrasesBankServerTextField.setText(
    			Settings.getString("phrasesBankServer"));
    	
    	phrasesBankPortLabel = new JLabel("Port:");
    	phrasesBankPortTextField = new JTextField();
    	LimitedPlainDocument phrasesBankPortDocument = 
			new LimitedPlainDocument(5);
    	phrasesBankPortDocument.addDocumentListener(documentListener);
    	phrasesBankPortTextField.setDocument(phrasesBankPortDocument);
    	phrasesBankPortTextField.setText(Settings.getString("phrasesBankPort"));
    	testPhrasesBankButton = new JButton("Test server");
    	testPhrasesBankLabel = new JLabel();

    	
    	phrasesBankProxyPanel = new JPanel();
    	phrasesBankProxyPanel.setBorder(BorderFactory.
				createTitledBorder(BorderFactory.
				createEtchedBorder(), "Proxy"));
    	phrasesBankUseProxyCheckBox = new JCheckBox("use proxy server");
 
    	phrasesBankProxyServerLabel = new JLabel("Server:");
    	phrasesBankProxyServerTextField = new JTextField();
    	LimitedPlainDocument phrasesBankProxyServerDocument = 
			new LimitedPlainDocument(200);
    	phrasesBankProxyServerDocument.addDocumentListener(documentListener);
    	phrasesBankProxyServerTextField.setDocument(
    			phrasesBankProxyServerDocument);
    	phrasesBankProxyServerTextField.setText(Settings.getString(
    			"phrasesBankProxyServer"));
    	
    	phrasesBankProxyPortLabel = new JLabel("Port:");
    	phrasesBankProxyPortTextField = new JTextField();
    	LimitedPlainDocument phrasesBankProxyPortDocument = 
			new LimitedPlainDocument(5);
    	phrasesBankProxyPortDocument.addDocumentListener(documentListener);
    	phrasesBankProxyPortTextField.setDocument(phrasesBankProxyPortDocument);
    	phrasesBankProxyPortTextField.setText(
    			Settings.getString("phrasesBankProxyPort"));
    	
    	testPhrasesBankButton.addActionListener(
			    new AbstractAction() {
			        public void actionPerformed(ActionEvent ev) {
			        	if(!validateCurrentPanel()) {
			    			showErrorMessage();
			    			return;
			    		}	
			        	boolean useProxy;
			        	String proxyHost;
			        	int proxyPort;
			        	if(phrasesBankUseProxyCheckBox.isSelected()) {
			        		useProxy = true;
			        		proxyHost = 
			        			phrasesBankProxyServerTextField.getText().trim();
			        		proxyPort =  Integer.parseInt(
				        			phrasesBankProxyPortTextField.getText()
				        					.trim());
			        	} else {
			        		useProxy = false;
			        		proxyHost = "";
							proxyPort = 0;
			        	}
						
			        	if(storage.testPhrasesBankConnection(
			        			phrasesBankServerTextField.getText(),
			        			phrasesBankPortTextField.getText(), 
			        			useProxy,
			        			proxyHost,
			        			proxyPort)) {
			        		testPhrasesBankLabel.setText(
			        				"Server tested successfully");
			        	} else {
			        		testPhrasesBankLabel.setText(
			        			"Could not connect to server");
			        	}
			        	//Settings.refreshProxySystemProperties();
			        }
			    });
    	
    	DocumentListener serverListener = new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				testPhrasesBankLabel.setText("");
			}
			
			public void insertUpdate(DocumentEvent e) {
				testPhrasesBankLabel.setText("");
			}
			
			public void removeUpdate(DocumentEvent e) {
				testPhrasesBankLabel.setText("");
			}
		};
		phrasesBankServerDocument.addDocumentListener(serverListener);
    	phrasesBankPortDocument.addDocumentListener(serverListener);
    	
       	phrasesBankUseProxyCheckBox.addChangeListener(
    		    new ChangeListener() {
    		    	public void stateChanged(ChangeEvent e) {
    		    		boolean selected = 
    		    			phrasesBankUseProxyCheckBox.isSelected();
    		    		phrasesBankProxyServerTextField.setEditable(selected);
			        	phrasesBankProxyPortTextField.setEditable(selected);
			        }
			    });
       	boolean useProxy = Settings.getBoolean("phrasesBankUseProxy");
    	phrasesBankUseProxyCheckBox.setSelected(useProxy);    
    	phrasesBankProxyServerTextField.setEditable(useProxy);
    	phrasesBankProxyPortTextField.setEditable(useProxy);
    }
    
    
    /**
     * Initializes Test Settings panel
     */
    private void initTestPanel() {
    	settingsTestPanel = new JPanel();  
    	checkTestPanel = new JPanel();
    	checkTestPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), "Check"));
    	checkPeriodExplanationLabel = 
    		new JLabel("<html>Every 'Test needs check period' minutes " +
    				"Crammer analyses " +
    				"through the words if they should be tested.</html>");    	
    	checkPeriodLabel = new JLabel("Test needs check period (in minutes):");
    	checkPeriodTextField = new JTextField();
    	LimitedPlainDocument checkPeriodDocument = new LimitedPlainDocument(4);
    	checkPeriodDocument.addDocumentListener(documentListener);
    	checkPeriodTextField.setDocument(checkPeriodDocument);
    	checkPeriodTextField.setText(
    			Long.toString((
    					Settings.getLong("testNeedsCheckPeriod")/60000))); 
    	   	
    	notifyTestPanel = new JPanel();
    	notifyTestPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), "Notification"));
    	neededToTestWindowExplanationLabel = 
    		new JLabel("<html>If during the check Crammer collects certain number " +
    				"of untested words it can show message notifying " +
    				"that there are words to test.</html>");
    	showNeededToTestWindowCheckBox = 
			new JCheckBox("show floating message", 
	    			Settings.getBoolean("showNeededToTestWindow"));
    	neededToTestWindowNumberLabel = new JLabel(
    			"Number of untested words (for one test):");;
    	neededToTestWindowNumberTextField = new JTextField();
    	LimitedPlainDocument neededToTestWindowNumberDocument = new LimitedPlainDocument(4);
    	neededToTestWindowNumberDocument.addDocumentListener(documentListener);
    	neededToTestWindowNumberTextField.setDocument(neededToTestWindowNumberDocument);
    	
    	neededToTestWindowNumberTextField.setText(
    			Integer.toString(Settings.getInt("neededToTestWindowNumber")));
    }   
      
    
    /**
     * Initializes Transcription panel
     */
    private void initTranscriptionPanel() {
    	settingsTranscriptionPanel = new JPanel();
    	
    	transcriptionLinesNumberLabel = new JLabel("Number of lines shown in test dialog:");
    	transcriptionLinesNumberTextField = new JTextField();    	
    	LimitedPlainDocument transcriptionLinesNumberDocument = new LimitedPlainDocument(2);
    	transcriptionLinesNumberDocument.addDocumentListener(documentListener);
    	transcriptionLinesNumberTextField.setDocument(transcriptionLinesNumberDocument);
    	transcriptionLinesNumberTextField.setText(
    			Settings.getString("transcriptionNumberOfLines"));
    	
    	transcriptionParametersPanel = new JPanel();
    	transcriptionParametersPanel.setBorder(BorderFactory.
    								createTitledBorder(BorderFactory.
    								createEtchedBorder(), "Repeat time parameters"));
    	transcriptionAlgorithmLabel = new JLabel(REPEAT_ALG_DESCR);
    	transcriptionBasePeriodLabel = new JLabel("BasePeriod (in minutes):");
    	transcriptionBasePeriodTextField = new JTextField();
    	LimitedPlainDocument transcriptionBasePeriodDocument = 
				new LimitedPlainDocument(10);
    	transcriptionBasePeriodDocument.addDocumentListener(documentListener);
    	transcriptionBasePeriodTextField.setDocument(transcriptionBasePeriodDocument);
    	transcriptionBasePeriodTextField.setText(
    			Long.toString((
    					Settings.getLong("transcriptionBasePeriod")/60000)));    	
    	
    	transcriptionFactorLabel = new JLabel("Factor:");
    	transcriptionFactorTextField = new JTextField();
    	LimitedPlainDocument transcriptionFactorDocument = 
				new LimitedPlainDocument(10);
    	transcriptionFactorDocument.addDocumentListener(documentListener);
    	transcriptionFactorTextField.setDocument(transcriptionFactorDocument);
    	transcriptionFactorTextField.setText(
    			Settings.getString("transcriptionFactor"));
    }
    
	/**
	 * Implements ProgressBar Interface isPBVisible function
	 */
	public boolean isPBVisible() {
		return progressBar.isVisible();
	}
    
    /**
     * Ok button action handler. Saves all entered settings.
     */
    private void okButtonAction() {
    	if(!validateCurrentPanel()) {
			showErrorMessage();
			return;
		}	
    	
    	Settings.setBoolean("minimizeToTray", 
    			minimizeToTrayCheckBox.isSelected());
    	Settings.setBoolean("minimizeOnClose", 
    			minimizeOnCloseCheckBox.isSelected());
    	Settings.setLong("testNeedsCheckPeriod", 
    			Long.parseLong(checkPeriodTextField.getText())*60000);
    	Settings.setBoolean("showNeededToTestWindow", 
    			showNeededToTestWindowCheckBox.isSelected());
    	Settings.setInt("neededToTestWindowNumber", 
    	    			Integer.parseInt(neededToTestWindowNumberTextField.getText()));
    	Settings.setInt("translationNumberOfLines", 
    			Integer.parseInt(translationLinesNumberTextField.getText()));
    	Settings.setLong("translationBasePeriod", 
    			Long.parseLong(
    					translationBasePeriodTextField.getText())*60000);
    	Settings.setDouble("translationFactor", 
    			Double.parseDouble(translationFactorTextField.getText()));
    	Settings.setInt("reverseNumberOfLines", 
    			Integer.parseInt(reverseLinesNumberTextField.getText()));
    	Settings.setLong("reverseBasePeriod", 
    			Long.parseLong(
    					reverseBasePeriodTextField.getText())*60000);
    	Settings.setDouble("reverseFactor", 
    			Double.parseDouble(reverseFactorTextField.getText()));
    	Settings.setInt("transcriptionNumberOfLines", 
    			Integer.parseInt(transcriptionLinesNumberTextField.getText()));
    	Settings.setLong("transcriptionBasePeriod", 
    			Long.parseLong(
    					transcriptionBasePeriodTextField.getText())*60000);
    	Settings.setDouble("transcriptionFactor", 
    			Double.parseDouble(transcriptionFactorTextField.getText()));
    	Settings.setInt("gapsFillingNumberOfLines", 
    			Integer.parseInt(gapsFillingLinesNumberTextField.getText()));
    	Settings.setLong("gapsFillingBasePeriod", 
    			Long.parseLong(
    					gapsFillingBasePeriodTextField.getText())*60000);
    	Settings.setDouble("gapsFillingFactor", 
    			Double.parseDouble(gapsFillingFactorTextField.getText()));
    	Settings.setBoolean("addPhrasePoint", 
				addPhrasePointCheckBox.isSelected());
    	Settings.setBoolean("addPhraseExclamation", 
				addPhraseExclamationCheckBox.isSelected());
    	Settings.setBoolean("addPhraseQuestion", 
				addPhraseQuestionCheckBox.isSelected());
    	Settings.setBoolean("addPhraseLF", 
				addPhraseLFCheckBox.isSelected());
    	Settings.setString("addPhraseCustomDelimiter",
    			addPhraseCustomDelimiterTextField.getText());
    	Settings.setInt("addPhraseMinLength",
    			Integer.parseInt(addPhraseMinLengthTextField.getText()));
    	Settings.setBoolean("addPhrasePreprocessing", 
    			addPhrasePreprocessingCheckBox.isSelected());   	
    	Settings.setBoolean("loadPhrasesFromBankForNewWords", 
    			loadPhrasesFromBankCheckBox.isSelected());
    	Settings.setString("phrasesBankServer",
    			phrasesBankServerTextField.getText());
    	Settings.setString("phrasesBankPort",
    			phrasesBankPortTextField.getText());
    	Settings.setBoolean("phrasesBankUseProxy", 
    			phrasesBankUseProxyCheckBox.isSelected());
    	Settings.setString("phrasesBankProxyServer",
    			phrasesBankProxyServerTextField.getText());
    	Settings.setString("phrasesBankProxyPort",
    			phrasesBankProxyPortTextField.getText());

    	//Settings.refreshProxySystemProperties();
        pressedButton = BTN_OK;
        setVisible(false);	
    }
      
    
    /**
     * Places UI components using different layout managers
     */
    private void placeComponents() {
    	mainPanel.setLayout(new BorderLayout());
    	mainPanel.add(treeScrollPane, BorderLayout.WEST);
    	mainPanel.add(centerPanelWithBorders, BorderLayout.CENTER);
    	mainPanel.add(bottomPanelWithBorders, BorderLayout.SOUTH);
    	
    	treeScrollPane.setPreferredSize(new Dimension(180,100));
    	    	 
    	centerPanelWithBorders.setLayout(new BorderLayout());
    	centerPanelWithBorders.add(centerPanelLeftBorder, BorderLayout.WEST);
    	centerPanelWithBorders.add(centerPanel, BorderLayout.CENTER);
    	centerPanelLeftBorder.setPreferredSize(new Dimension(2, 2));
    	
    	centerPanel.setLayout(new BorderLayout());
    	centerPanel.add(settingsNamePanelWithBorders, BorderLayout.NORTH);
    	centerPanel.add(settingsPanel, BorderLayout.CENTER);
    	centerPanel.add(centerBottomPanel, BorderLayout.SOUTH);

    	settingsNamePanelWithBorders.setPreferredSize(new Dimension(200, 40));
    	settingsNamePanelWithBorders.setLayout(new BorderLayout());
    	settingsNamePanelWithBorders.add(settingsNamePanelBottomBorder, BorderLayout.SOUTH);
    	settingsNamePanelWithBorders.add(settingsNamePanel, BorderLayout.CENTER);
    	settingsNamePanelBottomBorder.setPreferredSize(new Dimension(2, 2));    	
    	
    	settingsNamePanel.setLayout(null);
    	settingsNamePanel.add(settingsNameLabel);
    	settingsNamePanel.add(errorLabel);
    	
    	settingsNameLabel.setBounds(13, 10, 200, 20);
    	errorLabel.setBounds(13, 10, 350, 20);
    	
    	settingsPanel.setLayout(new CardLayout());
    	
    	settingsPanel.add(settingsGeneralPanel, NAME_GENERAL);
    	settingsPanel.add(settingsTestPanel, NAME_TEST);
    	settingsPanel.add(settingsTranslationPanel, NAME_TRANSLATION);
    	settingsPanel.add(settingsReversePanel, NAME_REVERSE);
    	settingsPanel.add(settingsTranscriptionPanel, NAME_TRANS);
    	settingsPanel.add(settingsGapsFillingPanel, NAME_GAPS_FIL);
    	settingsPanel.add(settingsCorpusPanel, NAME_CORPUS);
    	settingsPanel.add(settingsAddPhrasePanel, NAME_ADD_PHRASES);
    	settingsPanel.add(settingsPhrasesBankPanel, NAME_PHRASES_BANK);
    	
    	// General settings panel
    	settingsGeneralPanel.setLayout(null);
    	settingsGeneralPanel.add(minimizeToTrayCheckBox);
    	settingsGeneralPanel.add(minimizeOnCloseCheckBox);
    	
    	minimizeToTrayCheckBox.setBounds(10, 10, 300, 20);
    	minimizeOnCloseCheckBox.setBounds(10, 30, 300, 20);

    	// Test settings panel 	
    	settingsTestPanel.setLayout(null);
    	settingsTestPanel.add(checkTestPanel);
    	settingsTestPanel.add(notifyTestPanel);
    	
    	checkTestPanel.setBounds(10, 10, 380, 85); 
    	notifyTestPanel.setBounds(10, 100, 380, 105);  
    	
    	checkTestPanel.setLayout(null);
    	checkTestPanel.add(checkPeriodExplanationLabel);
    	checkTestPanel.add(checkPeriodLabel);
    	checkTestPanel.add(checkPeriodTextField);
    	checkPeriodExplanationLabel.setBounds(15, 15, 350, 40);
    	checkPeriodLabel.setBounds(15, 55, 185, 20);
    	checkPeriodTextField.setBounds(200, 55, 90, 20);
    	
    	notifyTestPanel.setLayout(null);
    	notifyTestPanel.add(neededToTestWindowExplanationLabel);
    	notifyTestPanel.add(showNeededToTestWindowCheckBox);
    	notifyTestPanel.add(neededToTestWindowNumberLabel);    	
    	notifyTestPanel.add(neededToTestWindowNumberTextField);

    	neededToTestWindowExplanationLabel.setBounds(15, 15, 350, 40);
    	neededToTestWindowNumberLabel.setBounds(15, 55, 250, 20);
    	neededToTestWindowNumberTextField.setBounds(225, 55, 65, 20);
    	showNeededToTestWindowCheckBox.setBounds(15, 75, 160, 20);

    	// Translation Test settings panel
    	settingsTranslationPanel.setLayout(null);  	
    	settingsTranslationPanel.add(translationLinesNumberLabel);
    	settingsTranslationPanel.add(translationLinesNumberTextField);
    	settingsTranslationPanel.add(translationParametersPanel);
    	
    	translationLinesNumberLabel.setBounds(10, 10, 180, 20);
    	translationLinesNumberTextField.setBounds(195, 10, 105, 20);   	
    	translationParametersPanel.setBounds(10, 40, 380, 110);   	
    	translationParametersPanel.setLayout(null);
    	translationParametersPanel.add(translationAlgorithmLabel);
    	translationParametersPanel.add(translationBasePeriodLabel);
    	translationParametersPanel.add(translationBasePeriodTextField);
    	translationParametersPanel.add(translationFactorLabel);
    	translationParametersPanel.add(translationFactorTextField);
    	
    	translationAlgorithmLabel.setBounds(15, 15, 400, 40);
    	translationBasePeriodLabel.setBounds(15, 55, 150, 20);
    	translationBasePeriodTextField.setBounds(165, 55, 125, 20);
    	translationFactorLabel.setBounds(15, 80, 150, 20);
    	translationFactorTextField.setBounds(165, 80, 125, 20);
    	
    	// Reverse Translation Test settings panel
    	settingsReversePanel.setLayout(null);
    	settingsReversePanel.add(reverseLinesNumberLabel);
    	settingsReversePanel.add(reverseLinesNumberTextField);
    	settingsReversePanel.add(reverseParametersPanel);
    	
    	reverseLinesNumberLabel.setBounds(10, 10, 180, 20);
    	reverseLinesNumberTextField.setBounds(195, 10, 105, 20);  
    	reverseParametersPanel.setBounds(10, 40, 380, 110);
    	
    	reverseParametersPanel.setLayout(null);
    	reverseParametersPanel.add(reverseAlgorithmLabel);
    	reverseParametersPanel.add(reverseBasePeriodLabel);
    	reverseParametersPanel.add(reverseBasePeriodTextField);
    	reverseParametersPanel.add(reverseFactorLabel);
    	reverseParametersPanel.add(reverseFactorTextField);
    	
    	reverseAlgorithmLabel.setBounds(15, 15, 400, 40);
    	reverseBasePeriodLabel.setBounds(15, 55, 150, 20);
    	reverseBasePeriodTextField.setBounds(165, 55, 125, 20);
    	reverseFactorLabel.setBounds(15, 80, 150, 20);
    	reverseFactorTextField.setBounds(165, 80, 125, 20);

    	// Transcription Test settings panel
    	settingsTranscriptionPanel.setLayout(null);
    	settingsTranscriptionPanel.add(transcriptionLinesNumberLabel);
    	settingsTranscriptionPanel.add(transcriptionLinesNumberTextField);
    	settingsTranscriptionPanel.add(transcriptionParametersPanel);
    	
    	transcriptionLinesNumberLabel.setBounds(10, 10, 180, 20);
    	transcriptionLinesNumberTextField.setBounds(195, 10, 105, 20);  
    	transcriptionParametersPanel.setBounds(10, 40, 380, 110);
    	
    	transcriptionParametersPanel.setLayout(null);
    	transcriptionParametersPanel.add(transcriptionAlgorithmLabel);
    	transcriptionParametersPanel.add(transcriptionBasePeriodLabel);
    	transcriptionParametersPanel.add(transcriptionBasePeriodTextField);
    	transcriptionParametersPanel.add(transcriptionFactorLabel);
    	transcriptionParametersPanel.add(transcriptionFactorTextField);
    	
    	transcriptionAlgorithmLabel.setBounds(15, 15, 400, 40);
    	transcriptionBasePeriodLabel.setBounds(15, 55, 150, 20);
    	transcriptionBasePeriodTextField.setBounds(165, 55, 125, 20);
    	transcriptionFactorLabel.setBounds(15, 80, 150, 20);
    	transcriptionFactorTextField.setBounds(165, 80, 125, 20);

    	// Gaps Filling Test settings panel
    	settingsGapsFillingPanel.setLayout(null);
    	settingsGapsFillingPanel.add(gapsFillingLinesNumberLabel);
    	settingsGapsFillingPanel.add(gapsFillingLinesNumberTextField);
    	settingsGapsFillingPanel.add(gapsFillingParametersPanel);
    	
    	gapsFillingLinesNumberLabel.setBounds(10, 10, 180, 20);
    	gapsFillingLinesNumberTextField.setBounds(195, 10, 105, 20); 
    	gapsFillingParametersPanel.setBounds(10, 40, 380, 110);
    	
    	gapsFillingParametersPanel.setLayout(null);
    	gapsFillingParametersPanel.add(gapsFillingAlgorithmLabel);
    	gapsFillingParametersPanel.add(gapsFillingBasePeriodLabel);
    	gapsFillingParametersPanel.add(gapsFillingBasePeriodTextField);
    	gapsFillingParametersPanel.add(gapsFillingFactorLabel);
    	gapsFillingParametersPanel.add(gapsFillingFactorTextField);
    	 	
    	gapsFillingAlgorithmLabel.setBounds(15, 15, 400, 40);
    	gapsFillingBasePeriodLabel.setBounds(15, 55, 150, 20);
    	gapsFillingBasePeriodTextField.setBounds(165, 55, 125, 20);
    	gapsFillingFactorLabel.setBounds(15, 80, 150, 20);
    	gapsFillingFactorTextField.setBounds(165, 80, 125, 20);
    	
    	// Phrases corpus settings panel
    	settingsCorpusPanel.setLayout(new BorderLayout());
    	settingsCorpusPanel.add(corpusToolBar,BorderLayout.NORTH);
    	settingsCorpusPanel.add(corpusScrollPane, BorderLayout.CENTER);
    	settingsCorpusPanel.add(corpusBottomPanel, BorderLayout.SOUTH);
    	
    	corpusBottomPanel.setPreferredSize(new Dimension(200, 20)); 
    	corpusBottomPanel.setLayout(new BorderLayout());  	
    	corpusBottomPanel.add(corpusPhrasesCountLabel, BorderLayout.EAST);
    	corpusBottomPanel.add(progressBarPanel, BorderLayout.WEST);
    	
    	progressBarPanel.setPreferredSize(new Dimension(210, 20)); 
    	progressBarPanel.setLayout(null);
    	progressBarPanel.add(progressBarLabel);
    	progressBarPanel.add(progressBar);
    	
    	progressBarLabel.setBounds(5, 0, 100, 20);
    	progressBar.setBounds(105, 1, 100, 19);

    	// Adding phrases settings panel
    	settingsAddPhrasePanel.setLayout(null);
    	settingsAddPhrasePanel.add(addPhraseDelimitersPanel);
    	settingsAddPhrasePanel.add(addPhraseMinLengthLabel);
    	settingsAddPhrasePanel.add(addPhraseMinLengthTextField);
    	settingsAddPhrasePanel.add(addPhrasePreprocessingCheckBox);

		addPhraseDelimitersPanel.setBounds(10, 10, 380, 90);
		addPhraseDelimitersPanel.setLayout(null);
		addPhraseDelimitersPanel.add(addPhrasePointCheckBox);
		addPhraseDelimitersPanel.add(addPhraseExclamationCheckBox);
		addPhraseDelimitersPanel.add(addPhraseQuestionCheckBox);
		addPhraseDelimitersPanel.add(addPhraseLFCheckBox);
		addPhraseDelimitersPanel.add(addPhraseCustomDelimiterLabel);
		addPhraseDelimitersPanel.add(addPhraseCustomDelimiterTextField);
	    
	    addPhrasePointCheckBox.setBounds(15, 15, 150, 20);
	   	addPhraseExclamationCheckBox.setBounds(15, 35, 150, 20);
	   	addPhraseQuestionCheckBox.setBounds(170, 15, 150, 20);
	   	addPhraseLFCheckBox.setBounds(170, 35, 150, 20);
	   	addPhraseCustomDelimiterLabel.setBounds(15, 60, 150, 20);
	   	addPhraseCustomDelimiterTextField.setBounds(140, 60, 150, 20);

	   	addPhraseMinLengthLabel.setBounds(10, 130, 180, 20);
	   	addPhraseMinLengthTextField.setBounds(180, 130, 120, 20);
	   	addPhrasePreprocessingCheckBox.setBounds(10, 105, 380, 20);
	    	

    	// Phrases bank settings panel
    	settingsPhrasesBankPanel.setLayout(null);
    	settingsPhrasesBankPanel.add(loadPhrasesFromBankCheckBox);
    	settingsPhrasesBankPanel.add(phrasesBankServerPanel);
    	settingsPhrasesBankPanel.add(phrasesBankProxyPanel);
    	    	
    	loadPhrasesFromBankCheckBox.setBounds(10, 10, 300, 20);
    	
    	phrasesBankServerPanel.setBounds(10, 35, 380, 100);   	
    	phrasesBankServerPanel.setLayout(null);
    	phrasesBankServerPanel.add(phrasesBankServerLabel);
    	phrasesBankServerPanel.add(phrasesBankServerTextField);
    	phrasesBankServerPanel.add(phrasesBankPortLabel);
    	phrasesBankServerPanel.add(phrasesBankPortTextField);
    	phrasesBankServerPanel.add(testPhrasesBankButton);
    	phrasesBankServerPanel.add(testPhrasesBankLabel);
    					
    	phrasesBankServerLabel.setBounds(15, 20, 70, 20);
    	phrasesBankServerTextField.setBounds(70, 20, 230, 20);
    	phrasesBankPortLabel.setBounds(15, 45, 70, 20);
    	phrasesBankPortTextField.setBounds(70, 45, 130, 20);
    	testPhrasesBankButton.setBounds(70, 70, 100, 20);
    	testPhrasesBankLabel.setBounds(175, 70, 250, 20);
    	
    	phrasesBankProxyPanel.setBounds(10, 140, 380, 100);   	
    	phrasesBankProxyPanel.setLayout(null);
    	phrasesBankProxyPanel.add(phrasesBankUseProxyCheckBox);
    	phrasesBankProxyPanel.add(phrasesBankProxyServerLabel);
    	phrasesBankProxyPanel.add(phrasesBankProxyServerTextField);
    	phrasesBankProxyPanel.add(phrasesBankProxyPortLabel);
    	phrasesBankProxyPanel.add(phrasesBankProxyPortTextField);
    	
    	phrasesBankUseProxyCheckBox.setBounds(15, 15, 100, 20);
    	phrasesBankProxyServerLabel.setBounds(15, 40, 70, 20);
    	phrasesBankProxyServerTextField.setBounds(70, 40, 230, 20);
    	phrasesBankProxyPortLabel.setBounds(15, 65, 70, 20);
    	phrasesBankProxyPortTextField.setBounds(70, 65, 130, 20);

    	// Default button panel
    	centerBottomPanel.setPreferredSize(new Dimension(200, 30));
    	centerBottomPanel.setLayout(new BorderLayout());
    	centerBottomPanel.add(defaultButtonPanel, BorderLayout.EAST);
    	
    	defaultButtonPanel.setPreferredSize(new Dimension(135, 30));
    	defaultButtonPanel.setLayout(null);
    	defaultButtonPanel.add(defaultButton);
    	defaultButton.setBounds(10, 5, 115, 20);
    	
    	// Ok, Cancel buttons panel
    	bottomPanelWithBorders.setPreferredSize(new Dimension(200, 40));
    	bottomPanelWithBorders.setLayout(new BorderLayout());
    	bottomPanelWithBorders.add(bottomPanelTopBorder, BorderLayout.NORTH);
    	bottomPanelWithBorders.add(bottomPanel, BorderLayout.CENTER);
    	
    	bottomPanelTopBorder.setPreferredSize(new Dimension(2, 2));
    	
    	bottomPanel.setLayout(new BorderLayout());
    	bottomPanel.add(okCancelButtonsPanel, BorderLayout.EAST);
    	 
    	okCancelButtonsPanel.setPreferredSize(new Dimension(210, 40));
    	okCancelButtonsPanel.setLayout(null);
    	okCancelButtonsPanel.add(okButton);
    	okCancelButtonsPanel.add(cancelButton);
    	
    	okButton.setBounds(10, 10, 90, 20);    	
    	cancelButton.setBounds(110, 10, 90, 20);  	
    }
       
    
    /**
     * Enable/disable phrases batch toolbar buttons depending on 
     * selected batches.
     */
    public void refreshBatchesButtons() {
    	int index = corpusTable.getSelectedRow();
		boolean enableButton = (index >=0); 				
		corpusDeleteButton.setEnabled(enableButton);
		corpusEditButton.setEnabled(enableButton);;
		corpusExportButton.setEnabled(enableButton);;
    }
     
    
    /**
     * Refreshes statistics for phrases batches by requesting data 
     * from database. Right now all statistics is phrases count.
     */
    public void refreshStatistics() {
    	final SettingsDialog thisDialog = this;
		Thread thread = new Thread() {
			public void run() {
				corpus.loadStatistics(thisDialog);
				fireCorpusTableDataChanged();
				refreshBatchesButtons();
			}
		};
		thread.start();
    }
    
    
    /**
     * Sets visibility of panel with default button. Called when current
     * visible panel changes, because some of panels need default button
     * and some not.
     * @param value
     */
    private void setDefaultPanelVisible(boolean value) {
    	if (value) {
    		centerBottomPanel.setPreferredSize(new Dimension(200, 30));
    	} else {
    		centerBottomPanel.setPreferredSize(new Dimension(0,0));
    	}
    }
     
    
    /**
	 * Implements ProgressBar Interface setPBNote function
	 */
	public void setPBNote(final String note) {
   		if(!SwingUtilities.isEventDispatchThread()) {
   			SwingUtilities.invokeLater(new Runnable() {
   				public void run() {
   					progressBarLabel.setText(note);
   				}
   			});
  		} else {
  			progressBarLabel.setText(note);
  		}     
	}
	
	
    /** 
     * Initializes Settings tree
     */
    private void settingsTreeInit() {
        DefaultMutableTreeNode top = new DefaultMutableTreeNode();
        DefaultMutableTreeNode general = 
        	new DefaultMutableTreeNode(NAME_GENERAL);
        top.add(general);        
        DefaultMutableTreeNode test = 
        	new DefaultMutableTreeNode(NAME_TEST);
        DefaultMutableTreeNode translation = 
        	new DefaultMutableTreeNode(NAME_TRANSLATION);
        test.add(translation);
        DefaultMutableTreeNode reverse = 
        	new DefaultMutableTreeNode(NAME_REVERSE);
        test.add(reverse);
        DefaultMutableTreeNode transcription = 
        	new DefaultMutableTreeNode(NAME_TRANS);
        test.add(transcription);
        DefaultMutableTreeNode gapsFilling = 
        	new DefaultMutableTreeNode(NAME_GAPS_FIL);
        test.add(gapsFilling);   	
        DefaultMutableTreeNode corpusNode = 
        	new DefaultMutableTreeNode(NAME_CORPUS);
        gapsFilling.add(corpusNode);   	
        DefaultMutableTreeNode addPhrasesNode = 
        	new DefaultMutableTreeNode(NAME_ADD_PHRASES);
        corpusNode.add(addPhrasesNode);   	
        DefaultMutableTreeNode phrasesBankNode = 
        	new DefaultMutableTreeNode(NAME_PHRASES_BANK);
        corpusNode.add(phrasesBankNode);   	
        top.add(test);
        
        settingsTree = new JTree(top){     	
        	public void setSelectionPath(TreePath path) {
        		if(path != null) {
            		if(!validateCurrentPanel()) {
            			showErrorMessage();
            			return;
            		}           		
            	}	
        		super.setSelectionPath(path);
        	}
        };
        settingsTree.setBorder(BorderFactory.createEmptyBorder(7, 4, 0, 0));
        
        settingsTree.setRootVisible(false);
        settingsTree.setShowsRootHandles(true);
        settingsTree.getSelectionModel().setSelectionMode(
        		TreeSelectionModel.SINGLE_TREE_SELECTION);        
        settingsTree.addTreeSelectionListener(this);
               
        DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
        renderer.setLeafIcon(null);
        renderer.setClosedIcon(null);
        renderer.setOpenIcon(null);        
        settingsTree.setCellRenderer(renderer);
        settingsTree.expandRow(1);
        
        treeScrollPane = new JScrollPane(settingsTree);
        treeScrollPane.setBorder(null);
    }
    
    
    /**
     * Overwritten, so it saves window location when dialog is closed
     */
    public void setVisible(boolean v) {
    	if(v == false) {
	    	Settings.setInt("settingsDialogX", getX());
	    	Settings.setInt("settingsDialogY", getY());
	    	Settings.setInt("settingsDialogW", getWidth());
	    	Settings.setInt("settingsDialogH", getHeight());
    	}
    	super.setVisible(v);
    }
 
    
    /**
     * Shows error label. Used by validation functions.
     * @param error error string to show
     */
    private void showErrorLabel(String error) {
    	errorLabel.setVisible(true);
    	settingsNameLabel.setVisible(false);
    	errorLabel.setText(error);
    }
   
    
    /**
     * Show error message. Called when user tries to save some not valid
     * values.
     */
    private void showErrorMessage() {
    	JOptionPane.showMessageDialog(this, 
				"The currently displayed page contains invalid values.",
				"Could not accept changes", JOptionPane.ERROR_MESSAGE);
    }
      
    
    /**
     * Implements ProgressBar Interface showPBDeterminate function
     */
	public void showPBDeterminate(int max, String note) {
		progressBar.setMaximum(max*3);
		progressBar.setMinimum(0);
		progressBarValue = 0;
		progressBar.setValue(progressBarValue);
		progressBar.setVisible(true);
		progressBarLabel.setText(note);
		progressBarLabel.setVisible(true);
	}
    
	/**
	 * Implements ProgressBar Interface showPBIndeterminate function
	 */
    public void showPBIndeterminate(String note) {
		progressBar.setIndeterminate(true);
		progressBar.setVisible(true);
		progressBarLabel.setText(note);
		progressBarLabel.setVisible(true);
	}
    
    
    /**
     * Launches SpecifyBatchNameDialog. Used by functions of adding phrases
     * from directory, file or by hand.
     * @param initName initial name of batch (directory or file name)
     * @return PhrasesBatch that has specified name, or null if user pressed
     * 'Cancel' on SpecifyBatchNameDialog.
     */
    private PhrasesBatch specifyBatchName(String initName){
   		SpecifyBatchNameDialog nameDialog = new SpecifyBatchNameDialog(this, initName);
		nameDialog.setVisible(true);
	   	if(nameDialog.getPressedButton() == SpecifyBatchNameDialog.BTN_OK) {
	   		PhrasesBatch newBatch = new PhrasesBatch();
	   		newBatch.setName(nameDialog.getBatchName());
	   		return newBatch;
	   	}
	   	return null;
    }

    
	/**
	 * Implements ProgressBar Interface updatePBValue function
	 */
	public void updatePBValue(final int value) {
   		if(!SwingUtilities.isEventDispatchThread()) {
   			SwingUtilities.invokeLater(new Runnable() {
   				public void run() {
   					progressBar.setValue(value);
   				}
   			});
  		} else {
  			progressBar.setValue(value);
  		}      		
	}
    
	
	/**
	 * Validates components values on current visible panel
	 * @return true, if all values in componets on visible panel is valid
	 */
    public boolean validateCurrentPanel()
    {
		switch(currentPanel) {
		case PANEL_TEST: 
			return validateValue(checkPeriodTextField) &&
					validateValue(neededToTestWindowNumberTextField);
		case PANEL_TRANSLATION:
			return validateValue(translationLinesNumberTextField) &&
					validateValue(translationBasePeriodTextField) &&
					validateValue(translationFactorTextField);
		case PANEL_REVERSE:
			return validateValue(reverseLinesNumberTextField) &&
					validateValue(reverseBasePeriodTextField) &&
					validateValue(reverseFactorTextField);
		case PANEL_TRANS:
			return validateValue(transcriptionLinesNumberTextField) &&
					validateValue(transcriptionBasePeriodTextField) &&
					validateValue(transcriptionFactorTextField);
		case PANEL_GAPS_FIL:
			return validateValue(gapsFillingLinesNumberTextField) &&
					validateValue(gapsFillingBasePeriodTextField) &&
					validateValue(gapsFillingFactorTextField);
		case PANEL_ADD_PHRASES:
			if (!addPhrasePointCheckBox.isSelected() && 
					!addPhraseExclamationCheckBox.isSelected() &&
					!addPhraseQuestionCheckBox.isSelected() &&
					!addPhraseLFCheckBox.isSelected() &&
					addPhraseCustomDelimiterTextField.getText().trim().equals("")) {
				showErrorLabel(ERR_NO_DELIM);
    			return false;
			}
			return validateValue(addPhraseCustomDelimiterTextField) &&
					validateValue(addPhraseMinLengthTextField);
		case PANEL_PHRASES_BANK:
			boolean result = validateValue(phrasesBankPortTextField);
			if(phrasesBankUseProxyCheckBox.isSelected()) {
				result = result && validateValue(phrasesBankProxyPortTextField);
			}
			return result;
		default:
			return true;
		}
    }
  
	
    /**
     * Validates value of JTextComponent
     * @param component component to validate value
     * @return true if values of JTextComponent is valid
     */
	public boolean validateValue(JTextComponent component) {
    	Document document = component.getDocument();
    	
    	if(component == translationLinesNumberTextField ||
    			component == reverseLinesNumberTextField ||
    			component == transcriptionLinesNumberTextField ||
    			component == gapsFillingLinesNumberTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			int intValue = Integer.parseInt(content);
    			if(intValue < 1 || intValue > 40) {
    				showErrorLabel(ERR_TEST_NUMBER);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_TEST_NUMBER);
				return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == checkPeriodTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			long longValue = Long.parseLong(content);
    			if(longValue < 1) {
    				showErrorLabel(ERR_CHECK_PERIOD);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_CHECK_PERIOD);
				return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == neededToTestWindowNumberTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			int intValue = Integer.parseInt(content);
    			if(intValue < 1) {
    				showErrorLabel(ERR_NEEDED_TO_TEST_NUMBER);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_NEEDED_TO_TEST_NUMBER);
				return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == translationBasePeriodTextField || 
    			component == reverseBasePeriodTextField ||
    			component == transcriptionBasePeriodTextField ||
    			component == gapsFillingBasePeriodTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			long longValue = Long.parseLong(content);
    			if(longValue <0) {
    				showErrorLabel(ERR_REPEAT_TIME);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_REPEAT_TIME);
    			return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == translationFactorTextField || 
    			component == reverseFactorTextField ||
    			component == transcriptionFactorTextField ||
    			component == gapsFillingFactorTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			double doubleValue = Double.parseDouble(content);
    			if(doubleValue <0 || doubleValue > 10) {
    				showErrorLabel(ERR_BASE);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_BASE);
    			return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}

    	if(component == addPhraseCustomDelimiterTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			Pattern.compile(content);
    		}
    		catch(PatternSyntaxException pse) {
    			showErrorLabel(ERR_CUSTOM_DELIM);
    			return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == addPhraseMinLengthTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			int intValue = Integer.parseInt(content);
    			if(intValue <3 || intValue >15) {
    				showErrorLabel(ERR_MIN_PHRASE_LEN);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_MIN_PHRASE_LEN);
				return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	if(component == phrasesBankPortTextField || 
    			component == phrasesBankProxyPortTextField) {
    		try {
    			String content = document.getText(0, document.getLength());
    			int intValue = Integer.parseInt(content);
    			if(intValue < 1) {
    				showErrorLabel(ERR_BANK_PORT);
    				return false;
    			}
    		}
    		catch(NumberFormatException e) {
    			showErrorLabel(ERR_BANK_PORT);
				return false;
    		}
    		catch(BadLocationException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	// No errors
    	errorLabel.setVisible(false);
    	settingsNameLabel.setVisible(true);
    	return true;
    }
	
	
	/**
	 * Implements TreeSelectionListener interface valueChanged function.
	 * Sets visible custom panel depending on what tree element was selected.
	 */
	public void valueChanged(TreeSelectionEvent e) {
		DefaultMutableTreeNode node = 
    		(DefaultMutableTreeNode)settingsTree.getLastSelectedPathComponent();
    	
    	if (node == null) {
    		currentPanel = PANEL_NO;
    	    return;
    	}

    	String nodeName = (String)node.getUserObject();
    	settingsNameLabel.setText(nodeName);
    	CardLayout cardLayout = (CardLayout)(settingsPanel.getLayout());
    	cardLayout.show(settingsPanel, nodeName);
    	if(nodeName.equals(NAME_GENERAL)) {
    		currentPanel = PANEL_GENERAL;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_TEST)) {
    		currentPanel = PANEL_TEST;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_TRANSLATION)) {
    		currentPanel = PANEL_TRANSLATION;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_REVERSE)) {
    		currentPanel = PANEL_REVERSE;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_TRANS)) {
    		currentPanel = PANEL_TRANS;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_GAPS_FIL)) {
    		currentPanel = PANEL_GAPS_FIL;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	if(nodeName.equals(NAME_CORPUS)) {
    		currentPanel = PANEL_CORPUS;
    		setDefaultPanelVisible(false);   		
   			refreshStatistics();
    	}
    	if(nodeName.equals(NAME_ADD_PHRASES)) {
    		currentPanel = PANEL_ADD_PHRASES;
    		setDefaultPanelVisible(true);
    		return;
    	}
       	if(nodeName.equals(NAME_PHRASES_BANK)) {
    		currentPanel = PANEL_PHRASES_BANK;
    		setDefaultPanelVisible(true);
    		return;
    	}
    	   	
    	currentPanel = PANEL_NO;
    }
	
	
	/**
	 * PhrasesCorpusSelectionListener class represents selection listener 
     * for phrasesBatches table in SettingsDialog
	 * @author Konstantin Fadeyev
	 *
	 */
	private class PhrasesCorpusSelectionListener 
			implements ListSelectionListener {

		SettingsDialog dialog;
		
		public PhrasesCorpusSelectionListener(SettingsDialog d) {
		    dialog = d;
		}
		
		public void valueChanged(ListSelectionEvent e) {
			dialog.refreshBatchesButtons();
		}
	}
	
	
	/**
	 * PhrasesCorpusTableModel class represents table model
     * for phrasesBatches table in SettingsDialog
	 * @author Konstantin Fadeyev
	 *
	 */
	private class PhrasesCorpusTableModel extends AbstractTableModel {

		private PhrasesCorpus corpus;
		private SimpleDateFormat dateFormat; 

		public PhrasesCorpusTableModel(PhrasesCorpus corpus) {
			super();
			this.corpus = corpus;
			dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
		}

		public int getRowCount() {
	         return corpus.size();
		}

		public int getColumnCount() {
			return 3;
		}

		public String getColumnName(int num) {
			switch(num) {
				case 0:
					return "Batch name";
				case 1:
					return "Adding date";
				default:
	                return "Phrases count";
			}
		}

	    public boolean isCellEditable(int rowIndex, int columnIndex) {
			return false;
	    }

		public Object getValueAt(int row, int column) {
			switch(column) {
			case 0:
				if(row<corpus.size()) {
					return corpus.getBatch(row).getName();
				} else {
					return "";
				}
			case 1:
				if(row<corpus.size()) {
					long createdate = corpus.getBatch(row).getCreateDate();					
					return dateFormat.format(new Date(createdate));
				} else {
					return "";
				}
			default:
	            if(row<corpus.size()) {
	            	return new Integer(corpus.getBatch(row).getPhrasesCount());
				} else {
					return "";
				}
			}
		}

	    public Class<?> getColumnClass(int c) {
	        return getValueAt(0, c).getClass();
	    }
	}
}
